////////////////////////////////////////////////////////////////////////////////
// Filename: shadow.fx
////////////////////////////////////////////////////////////////////////////////

#define NUM_LIGHTS 6
//////////////
// MATRICES //
//////////////
matrix worldMatrix;
matrix viewMatrix;
matrix projectionMatrix;

matrix lightViewMatrix[NUM_LIGHTS];
matrix lightProjectionMatrix[NUM_LIGHTS];

//////////////
// TEXTURES //
//////////////
Texture2D shaderTexture;
Texture2D depthMapTexture[NUM_LIGHTS];

/////////////
// GLOBALS //
/////////////
float4 ambientColor;
float4 diffuseColor[NUM_LIGHTS];
float3 lightPosition[NUM_LIGHTS];
///////////////////
// SAMPLE STATES //
///////////////////
SamplerState SampleTypeClamp
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Clamp;
    AddressV = Clamp;
};

SamplerState SampleTypeWrap
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};


//////////////
// TYPEDEFS //
//////////////
struct VertexInputType
{
    float4 position : POSITION;
    float2 tex : TEXCOORD0;
    float3 normal : NORMAL;
};

struct PixelInputType
{
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;
    float3 normal : NORMAL;	
	float4 lightViewPosition[NUM_LIGHTS] : TEXCOORD1;
	float3 lightPos[NUM_LIGHTS] : TEXCOORD7;
};


////////////////////////////////////////////////////////////////////////////////
// Vertex Shader
////////////////////////////////////////////////////////////////////////////////
PixelInputType ShadowVertexShader(VertexInputType input)
{
    PixelInputType output;
    float4 worldPosition;
    
    
    // Change the position vector to be 4 units for proper matrix calculations.
    input.position.w = 1.0f;

    // Calculate the position of the vertex against the world, view, and projection matrices.
    output.position = mul(input.position, worldMatrix);
    output.position = mul(output.position, viewMatrix);
    output.position = mul(output.position, projectionMatrix);
    
    // Calculate the position of the vertice as viewed by the light source.
	for(int i = 0; i< NUM_LIGHTS;i++) {
		output.lightViewPosition[i] = mul(input.position, worldMatrix);
		output.lightViewPosition[i] = mul(output.lightViewPosition[i], lightViewMatrix[i]);
		output.lightViewPosition[i] = mul(output.lightViewPosition[i], lightProjectionMatrix[i]);
	}
    // Store the texture coordinates for the pixel shader.
    output.tex = input.tex;
    
    // Calculate the normal vector against the world matrix only.
    output.normal = mul(input.normal, (float3x3)worldMatrix);
	
    // Normalize the normal vector.
    output.normal = normalize(output.normal);

    // Calculate the position of the vertex in the world.
    worldPosition = mul(input.position, worldMatrix);

	for(int j = 0; j < NUM_LIGHTS;j++) {
		// Determine the light position based on the position of the light and the position of the vertex in the world.
		output.lightPos[j] = lightPosition[j].xyz - worldPosition.xyz;
		// Normalize the light position vector.
		output.lightPos[j] = normalize(output.lightPos[j]);
	}
    return output;
}


////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 ShadowPixelShader(PixelInputType input) : SV_Target
{
    float bias;
    float4 color;
    float2 projectTexCoord;
    float depthValue;
    float lightDepthValue;
    float lightIntensity;
    float4 textureColor;


    // Set the bias value for fixing the floating point precision issues.
    bias = 0.001f;

    // Set the default output color to the ambient light value for all pixels.
	color = ambientColor;
	//Calculate the color/shadow for the lights as normal except for that we don't saturate a final light color anymore.
	for(int i = 0; i< NUM_LIGHTS;i++) {
		
		// Calculate the projected texture coordinates.
		projectTexCoord.x =  input.lightViewPosition[i].x / input.lightViewPosition[i].w / 2.0f + 0.5f;
		projectTexCoord.y = -input.lightViewPosition[i].y / input.lightViewPosition[i].w / 2.0f + 0.5f;

		// Determine if the projected coordinates are in the 0 to 1 range.  If so then this pixel is in the view of the light.
		if((saturate(projectTexCoord.x) == projectTexCoord.x) && (saturate(projectTexCoord.y) == projectTexCoord.y))
		{
			color = diffuseColor[i];
			// Sample the shadow map depth value from the depth texture using the sampler at the projected texture coordinate location.
			depthValue = depthMapTexture[i].Sample(SampleTypeClamp, projectTexCoord).r;

			// Calculate the depth of the light.
			lightDepthValue = input.lightViewPosition[i].z / input.lightViewPosition[i].w;

			// Subtract the bias from the lightDepthValue.
			lightDepthValue = lightDepthValue - bias;

			// Compare the depth of the shadow map value and the depth of the light to determine whether to shadow or to light this pixel.
			// If the light is in front of the object then light the pixel, if not then shadow this pixel since an object (occluder) is casting a shadow on it.
			if(lightDepthValue < depthValue)
			{
				// Calculate the amount of light on this pixel.
				lightIntensity = saturate(dot(input.normal, input.lightPos[i]));
				if(lightIntensity > 0.0f)
				{
					// Determine the final diffuse color based on the diffuse color and the amount of light intensity.
					color += (diffuseColor[i] * lightIntensity);
				}
			}
		}
	}
	//color += (diffuseColor[0] * lightIntensity);
    // Saturate the final light color.
    color = saturate(color);

    // Sample the pixel color from the texture using the sampler at this texture coordinate location.
    textureColor = shaderTexture.Sample(SampleTypeWrap, input.tex);

    // Combine the light and texture color.
    color = color * textureColor;

    return color;
}

////////////////////////////////////////////////////////////////////////////////
// Technique
////////////////////////////////////////////////////////////////////////////////
technique10 ShadowTechnique
{
    pass pass0
    {
        SetVertexShader(CompileShader(vs_4_0, ShadowVertexShader()));
        SetPixelShader(CompileShader(ps_4_0, ShadowPixelShader()));
        SetGeometryShader(NULL);
    }
}